type LIFE_STATUS = 'init' | string

type HookFun = () => void

type CatchFun = (stopPoint: string, reason: any) => void

class LifecycleListener {
  private readonly onComplete: HookFun

  private onError?: CatchFun

  constructor(hook: HookFun) {
    this.onComplete = hook
  }

  public dispatchComplete() {
    this.onComplete()
  }

  public dispatchError(stopPoint: string, reason: any) {
    this.onError && this.onError(stopPoint, reason)
  }

  public catch(fun: CatchFun) {
    this.onError = fun
  }
}

export interface Stage {
  name: string
  method: (params?: any) => Promise<any>
  listeners?: LifecycleListener[]
}

export class AppLoginChain {
  private readonly stageArr: Stage[]

  private stopPoint = ''

  private hasStop = false

  private stopReason: any = null

  constructor(stageArr: Stage[]) {
    stageArr.forEach((stage) => {
      stage.listeners = []
    })
    this.stageArr = stageArr
  }

  private currentStatus: LIFE_STATUS = 'init'

  public restart = () => {
    this.currentStatus = 'init'
    this.stopPoint = ''
    this.hasStop = false
    this.stopReason = null
    this.stageArr.forEach((stage) => {
      stage.listeners = []
    })
    return this.initApp()
  }

  public initApp = async () => {
    if (this.currentStatus !== 'init') {
      return Promise.resolve()
    }

    const stageArr = this.stageArr

    //  很难木的住

    for (let i = 0; i < stageArr.length; i++) {
      const stage = stageArr[i]
      try {
        await stage.method()
        this.currentStatus = stage.name
        this.dispatchListenerComplete(stage.listeners!)
        stage.listeners!.length = 0
      } catch (error) {
        this.hasStop = true
        this.stopReason = error
        this.stopPoint = stage.name
        this.interruptChain(i, error)
        break
      }
    }

    return Promise.resolve()
  }

  private interruptChain(index: number, error: any) {
    const stageArr = this.stageArr

    for (let i = index; i < stageArr.length; i++) {
      const stage = stageArr[i]
      this.dispatchListenerError(stage.listeners!, stage.name, error)
      stage.listeners!.length = 0
    }
  }

  private dispatchListenerComplete = (lifecycleHookArr: LifecycleListener[]) => {
    lifecycleHookArr.forEach((listener) => {
      listener.dispatchComplete()
    })
  }

  private dispatchListenerError = (lifecycleHookArr: LifecycleListener[], stopPoint: string, reason: any) => {
    lifecycleHookArr.forEach((listener) => {
      listener.dispatchError(stopPoint, reason)
    })
  }

  private stageIsCompleted = (stageName: string) => {
    const currentIndex = this.findState(this.currentStatus)
    const stageIndex = this.findState(stageName)

    return currentIndex >= stageIndex
  }

  private findState = (name: string): number => {
    return this.stageArr.findIndex((e) => {
      return e.name === name
    })
  }

  public registerHooks = (name: string, hook: HookFun) => {
    const listener = new LifecycleListener(hook)

    const index = this.findState(name)

    if (index > -1) {
      console.log('this.currentStatus', this.currentStatus)

      if (this.stageIsCompleted(name)) {
        setTimeout(() => {
          hook()
        }, 0)
      } else if (this.hasStop) {
        setTimeout(() => {
          listener.dispatchError(this.stopPoint, this.stopReason)
        }, 0)
      } else {
        this.stageArr[index].listeners!.push(listener)
      }
    } else {
      setTimeout(() => {
        listener.dispatchError(name, name + ' not exist.')
      }, 0)
    }

    return listener
  }
}
